home *** CD-ROM | disk | FTP | other *** search
-
- #import "DebugFlags.h"
-
- #import <OpenTptModule.h>
- #import <OTDebug.h>
- #import "OTDCon.h"
- #import "Kitties_Module.h"
-
- #import <dlpi.h>
- #import <mistream.h>
-
- #import "DLPIUtils.h"
- static char* gStreamList = nil;
- static UInt32 gFlags = nil;
-
- enum {
- kStateInit,
- kStateSawCR1,
- kStateSawLF1,
- kStateSawCR2,
- kStateLookingForBody,
- kStateInBody,
- kStateInAngle,
- kStateInBodyAngle,
- kStateNotHTML,
- kStateSkipContentLength
- };
-
- static Boolean IsReadQ(queue_t* q)
- // Returns true if q is the read queue of a queue pair.
- {
- return ( (q->q_flag & QREADR) != 0 );
- }
-
- static Boolean IsWriteQ(queue_t* q)
- // Returns true if q is the write queue of a queue pair.
- {
- return ( (q->q_flag & QREADR) == 0 );
- }
-
- struct PerStreamData
- {
- OSType magic; // kQTunnelPerStreamDataMagic for debugging
- UInt32 currentState; // Current DLPI state, ie DL_UNATTACHED etc
- queue_t* readQ; // Read queue of this stream
- UInt8 http;
- UInt8 state;
- UInt8 lastHeader;
- major_t major;
- minor_t minor;
- };
- typedef struct PerStreamData PerStreamData, *PerStreamDataPtr;
-
-
- struct MoreKittiesPortData {
- OSType magic; // Should be kQTunnelPortMagic
- // [... extra info for port goes here ...]
- };
- typedef struct MoreKittiesPortData MoreKittiesPortData;
-
-
- static PerStreamDataPtr GetPerStreamData(queue_t* readOrWriteQ)
- // You can pass both the read or the write queue to this routine
- // because mi_open_comm sets up both q_ptr's to point to the
- // queue local data.
- //
- // Note that, in order to avoid the overhead of a function call,
- // you would normally use inline code (or a macro)
- // to get your per-stream data instead of using a separate function.
- // However I think the separate function makes things clearer.
- // I also acts as a central bottleneck for my debugging code.
- //
- // Environment: any standard STREAMS entry point
- {
- PerStreamDataPtr streamData;
-
- streamData = (PerStreamDataPtr) readOrWriteQ->q_ptr;
-
- OTAssert("GetPerStreamData: what streamData", streamData != nil);
- OTAssert("GetPerStreamData: Bad magic", streamData->magic == kQTunnelPerStreamDataMagic);
-
- return (streamData);
- }
-
- void *SetFilter( UInt32 flags )
- {
- gFlags = flags;
- }
-
-
- void
- ShouldDecode( void *buffer, UInt32 length, void *userData )
- {
- PerStreamData *streamData;
-
- if ( !length ) {
- streamData = GetPerStreamData((queue_t *)userData);
- streamData->http = false;
- }
- }
-
- static int MoreKittiesWritePut(queue_t* q, mblk_t* mp)
- // This routine is called by STREAMS when it has a message for our
- // module from upstream. Typically, this routine is a big case statement
- // that dispatches to our various message handling routines.
- //
- // Environment: standard STREAMS entry point
- {
- PerStreamData *streamData;
- mblk_t *parse_blk;
- OTAssert("MoreKittiesWritePut: Not the write queue", IsWriteQ(q) );
- // Dump_Packet_Type( "\n===>MoreKittiesWritePut", mp );
- // Dump_Packet( mp );
- streamData = GetPerStreamData(q);
-
- putnext( q, mp );
- return 0;
- }
-
- static void Output( queue_t *q, char *output, UInt32 outputLength )
- {
- mblk_t *mp = 0;
- mp = allocb( outputLength, 0 );
- BlockMoveData( output, mp->b_rptr, outputLength );
- mp->b_wptr = mp->b_rptr + outputLength;
- putnext( q, mp );
- }
-
- #define hamster1string "<img src=\"http://www.stattenfield.org/humor/www.hamsterdance.com/hamu.gif\">\n"
- #define hamster2string "<img src=\"http://www.stattenfield.org/humor/www.hamsterdance.com/anin.gif\">\n"
- #define hamster3string "<img src=\"http://www.stattenfield.org/humor/www.hamsterdance.com/gerbil.gif\">\n"
- #define hamster4string "<img src=\"http://www.stattenfield.org/humor/www.hamsterdance.com/hamwalk.gif\">\n"
-
- #define kitten1string "<img src=\"http://www.tweekitten.com/Graphics/tkcat.gif\">\n"
- #define kitten2string "<img src=\"http://www.pets.com/images/ui/persian6_if_061900.jpg\">\n"
-
- static void SendHamster( queue_t *q )
- {
- if ( (gFlags & kHamsters) && !(rand() % 10) ) {
- switch ( rand() %4 ) {
- case 0: Output( q, hamster1string, sizeof(hamster1string) ); break;
- case 1: Output( q, hamster2string, sizeof(hamster2string) ); break;
- case 2: Output( q, hamster3string, sizeof(hamster3string) ); break;
- case 3: Output( q, hamster4string, sizeof(hamster4string) ); break;
- }
- }
- if ( (gFlags & kKittiesFlag) && !(rand() % 10) ) {
- switch ( rand() %2 ) {
- case 0: Output( q, kitten1string, sizeof(kitten1string) ); break;
- case 1: Output( q, kitten2string, sizeof(kitten2string) ); break;
- }
- }
- }
-
- #define header1String "<b>"
- #define header1String2 "</b>"
- #define header2String "<i>"
- #define header2String2 "</i>"
- #define header3String "<H1>"
- #define header3String2 "</H1>"
- #define header4String "<H6>"
- #define header4String2 "</H6>"
- #define header5String "<underline>"
- #define header5String2 "</underline>"
-
- static void SendHeader( queue_t *q, PerStreamData *streamData )
- {
- if ( streamData->lastHeader ) {
- switch( streamData->lastHeader ) {
- case 1: Output( q, header1String2, sizeof(header1String2) ); break;
- case 2: Output( q, header2String2, sizeof(header2String2) ); break;
- case 3: Output( q, header3String2, sizeof(header3String2) ); break;
- case 4: Output( q, header4String2, sizeof(header4String2) ); break;
- case 5: Output( q, header5String2, sizeof(header5String2) ); break;
- }
- }
- if ( (gFlags & kHeaders) ) {
- streamData->lastHeader = rand() % 5 + 1;
- switch( streamData->lastHeader ) {
- case 1: Output( q, header1String, sizeof(header1String) ); break;
- case 2: Output( q, header2String, sizeof(header2String) ); break;
- case 3: Output( q, header3String, sizeof(header3String) ); break;
- case 4: Output( q, header4String, sizeof(header4String) ); break;
- case 5: Output( q, header5String, sizeof(header5String) ); break;
- }
- }
- }
-
- static void Parse( queue_t *q, PerStreamData *streamData, unsigned char *data, UInt32 length )
- {
- UInt32 count;
-
- char output[1460];
- UInt32 outputLength;
- char contentLength[150];
- UInt32 contentLengthLength;
-
- outputLength = 0;
- if ( !strncmp( "HTTP/1", data, 6 ) ) {
- streamData->state = kStateInit;
- }
-
- for ( count = 0; count < length; count++ ) {
- switch ( streamData->state ) {
- case kStateInit:
- if ( data[count] == 13 ) {
- streamData->state = kStateSawCR1;
- }
- output[outputLength++] = data[count];
- break;
- case kStateSawCR1:
- if ( data[count] == 10 ) {
- streamData->state = kStateSawLF1;
- } else {
- streamData->state = kStateInit;
- }
- output[outputLength++] = data[count];
- break;
- case kStateSawLF1:
- //dprintmem( data+count, 20 );
- if ( data[count] == 'C' ) {
- if ( !strncmp( data+count, "Content-Type: ", 14 ) ) {
- if ( strncmp( data+count+14, "text/html", 9 ) ) {
- streamData->state = kStateNotHTML;
- } else {
- streamData->state = kStateInit;
- }
- } else if ( !strncmp( data+count, "Content-Length: ", 16 ) ) {
- // streamData->state = kStateSkipContentLength;
- contentLengthLength = 0;
- contentLength[contentLengthLength++] = data[count];
- // continue;
- }
- } else if ( data[count] == 13 ) {
- streamData->state = kStateSawCR2;
- } else {
- streamData->state = kStateInit;
- }
- output[outputLength++] = data[count];
- break;
- case kStateSawCR2:
- if ( data[count] == 10 ) {
- streamData->state = kStateLookingForBody;
- } else {
- streamData->state = kStateInit;
- }
- output[outputLength++] = data[count];
- if ( outputLength >= 1460 ) {
- Output( q, output, outputLength );
- outputLength = 0;
- }
- break;
- case kStateLookingForBody:
- if ( (data[count] == '<') &&
- (count < length - 6)) {
- if ( ((data[count+1] == 'B') || (data[count+1] == 'b')) &&
- ((data[count+2] == 'O') || (data[count+2] == 'o')) &&
- ((data[count+3] == 'D') || (data[count+3] == 'd')) &&
- ((data[count+4] == 'Y') || (data[count+4] == 'y')) ) {
- streamData->state = kStateInBodyAngle;
- }
-
- }
- output[outputLength++] = data[count];
- if ( outputLength >= 1460 ) {
- Output( q, output, outputLength );
- outputLength = 0;
- }
- break;
- case kStateInBodyAngle:
- output[outputLength++] = data[count];
- switch ( data[count] ) {
- case '>':
- Output( q, output, outputLength );
- outputLength = 0;
- streamData->state = kStateInBody;
- SendHamster( q );
- break;
- }
- if ( outputLength >= 1460 ) {
- Output( q, output, outputLength );
- outputLength = 0;
- }
- break;
- case kStateInBody:
- switch ( data[count] ) {
- case '<': streamData->state = kStateInAngle; break;
- case ' ':
- output[outputLength++] = data[count];
- Output( q, output, outputLength );
- outputLength = 0;
- streamData->state = kStateInBody;
- SendHeader( q, streamData );
- break;
- }
- output[outputLength++] = data[count];
- if ( outputLength >= 1460 ) {
- Output( q, output, outputLength );
- outputLength = 0;
- }
- break;
- case kStateInAngle:
- switch ( data[count] ) {
- case '>':
- output[outputLength++] = data[count];
- Output( q, output, outputLength );
- outputLength = 0;
- streamData->state = kStateInBody;
- SendHamster( q );
- break;
- default:
- output[outputLength++] = data[count];
- break;
- }
- break;
- case kStateNotHTML:
- output[outputLength++] = data[count];
- if ( outputLength >= 1460 ) {
- Output( q, output, outputLength );
- outputLength = 0;
- }
- break;
- case kStateSkipContentLength:
- contentLength[contentLengthLength++] = data[count];
- if ( data[count] == 13 ) {
- count++;
- streamData->state = kStateInit;
- }
- }
- }
- Output( q, output, outputLength );
- outputLength = 0;
-
- }
-
- static int MoreKittiesReadPut(queue_t* q, mblk_t* mp)
- // This routine is called by STREAMS when it has a message for our
- // module from downstream. Typically, this routine is a big case statement
- // that dispatches to our various message handling routines.
- //
- // Environment: standard STREAMS entry point
- {
- PerStreamData *streamData;
- mblk_t *parse_blk;
- T_conn_con *conCon;
-
- OTAssert("MoreKitties: Not the read queue", IsReadQ(q) );
- //Dump_Packet_Type( "\n<---MoreKittiesReadPut", mp );
- //Dump_Packet( mp );
- streamData = GetPerStreamData(q);
-
- switch (mp->b_datap->db_type) {
- case M_PCPROTO:
- case M_PROTO:
- switch ( ( (union T_primitives*) mp->b_rptr)->type ) {
- case T_CONN_CON:
- conCon = (T_conn_con *)mp->b_rptr;
- if ( conCon && (conCon->RES_length) ) {
- if ( *(UInt16 *)(mp->b_rptr + conCon->RES_offset + 2 ) == 80 ) {
- streamData->http = true;
- streamData->state = kStateInit;
- } else {
- }
- }
- putnext( q, mp );
- break;
- default:
- putnext( q, mp );
- break;
- }
- break;
- case M_DATA:
- if ( streamData->http ) {
- for( parse_blk = mp; parse_blk; parse_blk=parse_blk->b_cont ) {
- Parse( q,
- streamData,
- parse_blk->b_rptr,
- parse_blk->b_wptr-parse_blk->b_rptr );
- }
- freemsg( mp );
- } else {
- putnext( q , mp );
- }
- break;
- default:
- putnext(q, mp);
- break;
- }
-
- return 0;
- }
-
- /////////////////////////////////////////////////////////////////////
- // Open routine
-
- static int MoreKittiesOpen(queue_t* rdq, dev_t* dev, int flag, int sflag, cred_t* creds)
- // This routine is called by STREAMS when a new stream is connected to
- // our module. The bulk of the work here is done by the Mentat helper
- // routine mi_open_comm.
- //
- // Environment: standard STREAMS entry point
- {
- int err;
- PerStreamDataPtr streamData;
-
- OTAssert("MoreKittiesOpen: Not the read queue", IsReadQ(rdq) );
-
- EntryPoint("MoreKittiesOpen\n");
-
- err = noErr;
-
- // If we already have per-stream data for this stream, the stream is being reopened.
- // In that case, we can just return.
- // Note that we can't call GetPerStreamData because it checks that streamData is not nil.
-
- if ( rdq->q_ptr != nil ) {
- goto done;
- }
-
- // Make sure we're being opened properly -- because we're a module we
- // require a MODOPEN.
-
- if ( (err == noErr) && (sflag != MODOPEN) ) {
- err = ENXIO;
- }
-
- // Use the mi_open_comm routine to allocate our per-stream data. Then
- // zero out the entire per-stream data record and fill out the fields
- // we're going to need.
-
- if (err == noErr) {
- err = mi_open_comm(&gStreamList, sizeof(PerStreamData), rdq, dev, flag, sflag, creds);
- if ( err == noErr ) {
- // Note that we can't call GetPerStreamData because the magic is not set up yet.
- streamData = (PerStreamDataPtr) rdq->q_ptr;
-
- OTMemzero(streamData, sizeof(PerStreamData));
-
- streamData->magic = 'MK ';
- streamData->currentState = DL_UNATTACHED;
- streamData->readQ = rdq;
- streamData->http = 0;
- streamData->major = getmajor(*dev);
- streamData->minor = getminor(*dev);
- //dopen( "MoreKitties LOG" );
-
- EntryPoint("MoreKittiesOpen success\n");
- }
- }
-
- done:
- return (err);
- }
-
- /////////////////////////////////////////////////////////////////////
- // Close routine
-
- static int MoreKittiesClose(queue_t* rdq, int flags, cred_t* credP)
- // This routine is called by STREAMS when a stream is being
- // disconnected from our module (ie closed). The bulk of the work
- // is done by the magic Mentat helper routine mi_close_comm.
- //
- // Environment: standard STREAMS entry point
- {
- #pragma unused(flags)
- #pragma unused(credP)
- PerStreamDataPtr streamData;
- UInt32 count;
- OTAssert("MoreKittiesClose: Not the read queue", IsReadQ(rdq) );
-
- EntryPoint("MoreKittiesClose\n");
-
- streamData = GetPerStreamData(rdq);
- EntryPoint("MoreKittiesClose success\n");
-
-
- (void) mi_close_comm(&gStreamList, rdq);
-
- return 0;
- }
-
-
-
- static struct module_info gModuleInfo =
- {
- 9990, // Module Number, only useful for debugging
- "MoreKittiesMod", // Name of module
- 0, // Minimum data size
- 1500, // Maximum data size •••
- 16384, // Hi water mark for queue •••
- 4096 // Lo water mark for queue •••
- };
-
- static struct qinit gReadInit =
- {
- MoreKittiesReadPut, // Put routine for "incoming" data
- nil, // Service routine for "incoming" data
- MoreKittiesOpen, // Our open routine
- MoreKittiesClose, // Our close routine
- nil, // No admin routine
- &gModuleInfo // Our module_info
- };
-
- static struct qinit gWriteInit =
- {
- MoreKittiesWritePut, // Put routine for client data
- nil, // Service routine for client data
- nil, // open field only used in read-side structure
- nil, // close field only used in read-side structure
- nil, // admin field only used in read-side structure
- &gModuleInfo // Our module_info
- };
-
- static struct streamtab theStreamTab =
- {
- &gReadInit, // Our read-side qinit structure
- &gWriteInit, // Our write-side qinit structure
- 0, // We are not a mux, so set this to nil
- 0 // We are not a mux, so set this to nil
- };
-
- /////////////////////////////////////////////////////////////////////
- // Macintosh-specific Static Structures
-
- static struct install_info theInstallInfo =
- {
- &theStreamTab, // Stream Tab pointer
- kOTModIsModule + kOTModUpperIsDLPI,
- // Tell OT that we are a module, not a driver.
- // Also set kOTModIsComplexDriver to indicate that
- // we use a configurator.
- SQLVL_MODULE, // Synchronization level, module level for the moment
- 0, // Shared writer list buddy
- 0, // Open Transport use - always set to 0
- 0 // Flag - always set to 0
- };
-
- /////////////////////////////////////////////////////////////////////
- // Prototypes for the exported routines below.
-
- extern Boolean InitStreamModule(MoreKittiesPortData *portInfo);
- extern void TerminateStreamModule(void);
- extern install_info* GetOTInstallInfo();
- extern void EncryptStreams( Boolean doEncryption );
-
- #pragma export list InitStreamModule, TerminateStreamModule, GetOTInstallInfo, SetFilter
-
-
-
-
- extern Boolean InitStreamModule(MoreKittiesPortData *portInfo)
- // Initialises the module before the first stream is opened.
- // Should return true if the module has started up correctly.
- //
- // Environment: Always called at SystemTask time.
- {
- Boolean result;
-
- EntryPoint("MoreKitties: InitStreamModule\n");
-
- // Since we've changed to a module, we don't get a valid portInfo parameter.
- // For the moment, I'm leaving the assert around, just to make a point!
-
- // OTAssert("QTunnel: InitStreamModule: This is not the portInfo we're looking for",
- // portInfo->magic == kQTunnelPortMagic)
-
- #pragma unused(portInfo)
-
- result = true;
-
- return result;
- }
-
- extern void TerminateStreamModule(void)
- // Shuts down the module after the last stream has been
- // closed.
- //
- // Environment: Always called at SystemTask time.
- {
- // It's an excellent idea to have the following in your code, just to make
- // sure you haven't left any streams open before you quit. In theory, OT
- // should not call you until the last stream has been closed, but in practice
- // this can happen if you use mi_detach to half-close a stream.
-
- OTAssert("MoreKitties: TerminateStreamModule: Streams are still active", gStreamList == nil);
-
- EntryPoint("MoreKitties: TerminateStreamModule\n");
- }
-
- extern install_info* GetOTInstallInfo()
- // Return pointer to install_info to STREAMS.
- {
- return &theInstallInfo;
- }
-